package edu.mtu.citizenscience.mushroommapper.activity;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.openswimsoftware.orm.model.list.ModelArrayList;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

import edu.mtu.citizenscience.cs.utils.gps.CitsciGPS;
import edu.mtu.citizenscience.mushroommapper.MMActivity;
import edu.mtu.citizenscience.mushroommapper.activity.main.EnableGPSDialog;
import edu.mtu.citizenscience.mushroommapper.activity.main.FieldGuideActivity;
import edu.mtu.citizenscience.mushroommapper.activity.main.FindActivity;
import edu.mtu.citizenscience.mushroommapper.activity.main.ManageReportsActivity;
import edu.mtu.citizenscience.mushroommapper.activity.main.SettingsActivity;
import edu.mtu.citizenscience.mushroommapper.activity.main.TutorialActivity;
import edu.mtu.citizenscience.mushroommapper.activity.main.ProgressDialog;
import edu.mtu.citizenscience.mushroommapper.json.FieldGuide;
import edu.mtu.citizenscience.mushroommapper.json.MushroomNames;
import edu.mtu.citizenscience.mushroommapper.json.fieldguide.GuideMushroom;
import edu.mtu.citizenscience.mushroommapper.json.mushroomnames.Family;
import edu.mtu.citizenscience.mushroommapper.json.mushroomnames.Genus;
import edu.mtu.citizenscience.mushroommapper.json.mushroomnames.Species;
import edu.mtu.citizenscience.mushroommapper.model.ModelFamily;
import edu.mtu.citizenscience.mushroommapper.model.ModelGuideMushroom;
import edu.mtu.citizenscience.mushroommapper.model.family.ModelGenus;
import edu.mtu.citizenscience.mushroommapper.model.family.genus.ModelSpecies;
import edu.mtu.citizenscience.mushroommapper.model.fieldguide.ModelGuideImage;
/**
 * @author Corbin Uselton <corbinu@cwuselto@mtu.edu>
 * @copyright (c) 2012 MichiganTech <http://www.mtu.edu>
 * @since 1.0
 * 
 * Main Mushroom Mapper Activity Responsible for primary
 * navigation and tutorial
 */
public class MainActivity extends MMActivity {
	// Name of file which stores the current imported mushroom db
	private static final String DB_VERSION_FILE = "DBVersionFile";
	
	// Dialog which shows DB update progress
	private ProgressDialog updateDialog = new ProgressDialog();
	// Dialog which shows message to enable GPS
	private EnableGPSDialog gpsEnableDialog = new EnableGPSDialog();
	
	/**
	 * @since 1.0
	 * 
	 * Runs task in the background to update the Fieldguide DB
	 */
	private class UpdateFieldDB extends AsyncTask<Void, Integer, Void> {

		/**
		 * Do the the DB update in the background
		 * 
		 * @param params - Void - does not need any params
		 * @return Void
		 */
		@Override
		protected Void doInBackground(Void... params) 
		{
			// run the field guide update
			this.updateFieldGuideDB();
			
			// return nothing
			return null;
		}
		
		/**
		 * Called by the background task to notify main thread of progress
		 * 
		 * @param progress - Integers - first progress int is the max progress and secound is the current progresss
		 * @return void
		 */
		@Override
		protected void onProgressUpdate(Integer... progress) 
		{
			// show the updateDialog on progress update (will do nothing if already showing)
			updateDialog.show(getSupportFragmentManager(), "dialog");
			// set max progress on the dialog progressbar
			updateDialog.setMax(progress[0]);
			// set the current progress on the progressbar
			updateDialog.updateProgress(progress[1]);
		}
		
		/**
		 * Called on the main thread after the background task completes
		 * 
		 * @param result - Void - result means nothing and will always be null
		 * @return void
		 */
		@Override
		protected void onPostExecute(Void result)
	    {
			// set progress dialog to display that we will now be adding mushroom families
			updateDialog.setProgressLabel("Adding Mushroom Families");
			// update the mushrooom db in the background
	        new UpdateMushroomDB().execute();
	    }
		
		/**
		 * Runs in the background updating the fieldguide DB
		 * 
		 * The fieldguide data can be found in /assets/json/field_guide.json
		 * 
		 * @param void
		 * @return void
		 */
		public void updateFieldGuideDB()
	    {
			// Load the db version file
	    	SharedPreferences versionStorage = getSharedPreferences(DB_VERSION_FILE, Context.MODE_PRIVATE);
	    	// get the current guide data version from the db version file
			int guideVersion = versionStorage.getInt("guide_version", 0);
			
	    	try 
			{
	    		// Load guide data into the field guide object from field_guide.json using Jackson JSON
				FieldGuide guideData =  new ObjectMapper().readValue(aq.getContext().getResources().getAssets().open("json/field_guide.json"), FieldGuide.class);
				
				// if the version of the guide data is newer then the current loaded version
				if (guideData.getVersion() > guideVersion)
				{
					// get the number of mushrooms for max progress
					int max = guideData.getMushrooms().length;
					
					// get the current guide mushrooms from the db and delete them
					new ModelArrayList<ModelGuideMushroom>(aq.getContext(), ModelGuideMushroom.class).delete();
					
					// set the current mushroom number to 0 for progress
					int num = 0;
					// Loop over the guide mushrooms
					for (GuideMushroom mushroom: guideData.getMushrooms())
					{
						// set the current progress with the max as the number of mushrooms add the current progress as the current mushroom
						publishProgress(max, num++);
						// create new guide mushroom model and add the propreties of the current mushroom
						ModelGuideMushroom mushroomModel = new ModelGuideMushroom(aq.getContext());
						mushroomModel.setDescription(mushroom.getDescription());
						mushroomModel.setFamily(mushroom.getFamily());
						mushroomModel.setGenus(mushroom.getGenus());
						mushroomModel.setSpecies(mushroom.getSpecies());
						mushroomModel.setName(mushroom.getName());
						// save the new mushroom model to the DB
						mushroomModel.save();
						// create a new guide image model
						ModelGuideImage image = new ModelGuideImage(aq.getContext());
						// add the image from the mushroom data
						image.setPath(mushroom.getImage());
						// add this image to the guide mushroom model
						image.setMushroom(mushroomModel);
						// save the image
						image.save();
					}
					
					// get an editor for the db version preference
					SharedPreferences.Editor editor = versionStorage.edit();
					// put the new guide version in the db
					editor.putInt("guide_version", guideData.getVersion());
					// save the preference file changes
					editor.commit();
				}
			} 
			catch (JsonParseException e) 
			{
				// will occur if the JSON file is invalid
				e.printStackTrace();
			} 
			catch (JsonMappingException e) 
			{
				// will occur if the objects the JSON is being mapped to are invalid
				e.printStackTrace();
			} 
			catch (IOException e) 
			{
				e.printStackTrace();
			}
	    }
	
	}
	
	/**
	 * @since 1.0
	 * 
	 * Runs task in the background to update the Mushroom DB
	 */
	private class UpdateMushroomDB extends AsyncTask<Void, Integer, Void> {

		/**
		 * Do the the DB update in the background
		 * 
		 * @param params - Void - does not need any params
		 * @return Void
		 */
		@Override
		protected Void doInBackground(Void... params) 
		{
			// run the mushroom update
			this.updateMushroomNameDB();
			
			// return nothing
			return null;
		}
		
		/**
		 * Called by the background task to notify main thread of progress
		 * 
		 * @param progress - Integers - first progress int is the max progress and secound is the current progresss
		 * @return void
		 */
		@Override
		protected void onProgressUpdate(Integer... progress) 
		{
			// show the updateDialog on progress update (will do nothing if already showing)
			updateDialog.show(getSupportFragmentManager(), "dialog");
			// set max progress on the dialog progressbar
			updateDialog.setMax(progress[0]);
			// set the current progress on the progressbar
			updateDialog.updateProgress(progress[1]);
		}
		
		/**
		 * Called by the background task to notify main thread of progress
		 * 
		 * @param progress - Integers - first progress int is the max progress and secound is the current progresss
		 * @return void
		 */
		@Override
		protected void onPostExecute(Void result)
	    {
			// update complete so dismiss the progress dialog
			updateDialog.dismiss();
	    }
		
		/**
		 * Runs in the background updating the mushroom DB
		 * 
		 * The mushroom data can be found in /assets/json/mushrooms.json
		 * 
		 * @param void
		 * @return void
		 */
		public void updateMushroomNameDB()
	    {	
	    	// Load the db version file
	    	SharedPreferences versionStorage = getSharedPreferences(DB_VERSION_FILE, Context.MODE_PRIVATE);
	    	// get the current mushroom data version from the db version file
			int mushroomNameVersion = versionStorage.getInt("mushroom_names_version", 0);
			
			try 
			{
				// Load mushroom data into the mushroom names object from mushrooms.json using Jackson JSON
				MushroomNames mushroomNameData =  new ObjectMapper().readValue(aq.getContext().getResources().getAssets().open("json/mushrooms.json"), MushroomNames.class);
				
				// if the version of the mushroom data is newer then the current loaded version
				if (mushroomNameData.getVersion() > mushroomNameVersion)
				{
					// get the number of families for max progress
					int max = mushroomNameData.getFamilies().length;
					
					// get the current families, genus, and species from the db and delete them
					new ModelArrayList<ModelFamily>(aq.getContext(), ModelFamily.class).delete();
					new ModelArrayList<ModelGenus>(aq.getContext(), ModelGenus.class).delete();
					new ModelArrayList<ModelSpecies>(aq.getContext(), ModelSpecies.class).delete();
					
					// set the current family number to 0 for progress
					int num = 0;
					// Loop over the families
					for (Family family: mushroomNameData.getFamilies())
					{
						// set the current progress with the max as the number of families add the current progress as the current family
						this.publishProgress(max, num++);
						// create a new family model
						ModelFamily familyModel = new ModelFamily(aq.getContext());
						// set the family name
						familyModel.setName(family.getName());
						// save the new family
						familyModel.save();
						Log.d("Mushroom", "Adding family " + family.getName());
						
						// Loop over the genera
						for (Genus genus: family.getGenera())
						{
							// create a new genus model
							ModelGenus genusModel = new ModelGenus(aq.getContext());
							// set the genus name
							genusModel.setName(genus.getName());
							// set the genus family
							genusModel.setFamily(familyModel);
							// save the new genus
							genusModel.save();
							Log.d("Mushroom", "Adding genus " + genus.getName());
					
							// Loop over the species
							for (Species species: genus.getSpecies())
							{
								// create a new species model
								ModelSpecies speciesModel = new ModelSpecies(aq.getContext());
								// set the species name
								speciesModel.setName(species.getName());
								// set the species genus
								speciesModel.setGenus(genusModel);
								// save the species
								speciesModel.save();
								Log.d("Mushroom", "Adding species " + species.getName());
							}
						}
						
					}
					
					// get an editor for the db version preference
					SharedPreferences.Editor editor = versionStorage.edit();
					// put the new mushrooms version in the db
					editor.putInt("mushroom_names_version", mushroomNameData.getVersion());
					// save the preference file changes
					editor.commit();
				}
			} 
			catch (JsonParseException e) 
			{
				// will occur if the JSON file is invalid
				e.printStackTrace();
			} 
			catch (JsonMappingException e) 
			{
				// will occur if the objects the JSON is being mapped to are invalid
				e.printStackTrace();
			} 
			catch (IOException e) 
			{
				e.printStackTrace();
			}
	    }
		
	}

	/**
	 * Main Activity creation handler
	 * 
	 * @param   savedInstanceState - Bundle - Bundle with launch info
	 * @return	void
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        
        this.setContentView(R.layout.main_activity);
        
        // Setup button handlers
        this.aq.id(R.id.new_find_button).clicked(this, "newFind");
        this.aq.id(R.id.tutorial_button).clicked(this, "viewTutorial");
        this.aq.id(R.id.field_guide_button).clicked(this, "viewFieldGuide");
        this.aq.id(R.id.reports_button).clicked(this, "viewManageReports");
        this.aq.id(R.id.settings_button).clicked(this, "viewSettings");
        
        // set update dialog title
        this.updateDialog.setTitle("Updating DB...");
        // set progress lable on the update dialog to currently updating guide mushrooms
        this.updateDialog.setProgressLabel("Adding Guide Mushrooms");
        // start updating the field guide db
        new UpdateFieldDB().execute();
    }
	
	/**
	 * Main Activity start handler
	 * 
	 * @param   void
	 * @return	void
	 */
	@Override
    protected void onStart() 
    {
        super.onStart();

        // check if GPS is not enabled
        if ( ! CitsciGPS.isEnabled(this)) 
        {
        	// show dialog to inform the user that they need to enable the GPS
        	this.gpsEnableDialog.show(this.getSupportFragmentManager(), "dialog");
        }
    }
	
	/**
	 * onClick for the New Find button.
	 * 
	 * @param void
	 */
	public void newFind() 
	{
		// start the find activity to log the new find
		this.startActivity(new Intent(this, FindActivity.class));
	}
	
	/**
	 * onClick for the Getting Started button.
	 * 
	 * @param void
	 */
	public void viewTutorial() 
	{
		// start the tutorial activity for new new users
		this.startActivity(new Intent(this, TutorialActivity.class));
	}
	
	/**
	 * onClick for the Field Guide button.
	 * 
	 * @param void
	 */
	public void viewFieldGuide() 
	{
		// start the field guide activity for identifying mushrooms
		this.startActivity(new Intent(this, FieldGuideActivity.class));
	}
	
	/**
	 * onClick for the Manage Reports button.
	 * 
	 * @param void
	 */
	public void viewManageReports() 
	{
		// start the manage reports activity
		this.startActivity(new Intent(this, ManageReportsActivity.class));
	}
	
	/**
	 * onClick for the Settings button.
	 * 
	 * @param void
	 */
	public void viewSettings() 
	{
		// start the settings activity
		this.startActivity(new Intent(this, SettingsActivity.class));
	}
    
}
